(function($){if($.weboo) return;
/*
 * weboo 1.0 - Flexible Interface
 *
#-~>weboo:class<~-#
 */
var path = {
	root	: "/weboo",
	ext	: ".o",
	styles : {},
	cur : "",
	// get a real url
	url : function(name){
		return this.root + name + (name.indexOf('.')==-1 ? this.ext : "");},
	/* get resource */
	rs : function(type, def, name, module){
		var m = (module ? module : this.cur)
		var src = this.root +  ( this.styles[m] ?
									this.styles[m]
									: "/style" )
						 + "/"+ type + "/" + name
						 + (name.indexOf('.')==-1 ? def : "");
		return src;
	},

	icon : function(name, module){
		return this.rs("icon",".gif",name,module);},

	btn : function(name, module){
		return this.rs("btn",".gif",name,module);},
	
	horn : function(name, module){
		return this.rs("horn",".gif",name,module);},
	
	pic : function(name, module){
		return this.rs("pic",".jpg",name,module);}
	
};

var debug = true;

function ajaxError(r){
	var data;
	try{
		data = $.httpData(r,"json");
	}catch(E){
		data = new Array({message:r.responseText});
	};
	/* find the error gasket */
	if($("#weboo_error").size()==0){
		var gk = $.weboo.pattern("weboo.pattern.error.gasket").render();
		$(document.body).append(gk);
		gk.css("left",($(document.body).width()-gk.width())/2).css("top",30);
		gk.resizable();
		gk.draggable();
	}
	/* get error view */
	var errView = weboo.view("weboo.core.list","error",{cached : true
								,patternName : "weboo.pattern.error.shell"
								,gasket	: "#weboo_error"
								,item : "weboo.pattern.error.item"});
	errView.data(data);
	if(!errView.jq)
		errView.redraw();
	errView.renderItems();
	$("#weboo_error").parent().parent().fadeIn(300);
};

/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 * weboo class, plug into jQuery object.
 */
var Weboo = function(){
	this.patterns = new Object();
	this.views = new Object();
	this.path = path;
};
Weboo.prototype = {
	root: function(root){path.root = root;},
	extesion: function(ext){path.ext = ext;},
	url: function(an){return path.url(an);},
	/*
	 * Pattern factory
	 */
	pattern: function(name,callback,params){
		if(!name) throw "Lack pattern name!";
		if(!this.undefined(this.patterns[name]))
			return new Pattern(this.patterns[name]);
		// if callback given, don't return pattern, but send as callback first param.
		if(this.isFunction(callback)){
			$.ajax({
					url			: path.url("/weboo/getPattern")
					,data		: {name:name}
					,processData: true
					,cache		: !debug
					,async		: true
					,type		: "GET"
					,dataType	: "text"
					,success	: function(s){
						this.patterns[name] = s;
						callback(new Pattern(s),params);
					}
				});
		}
		// It will return the pattern Object after request succeed
		else{
			var oRequest = $.ajax({
					url			: path.url("/weboo/getPattern")
					,data		: {name:name}
					,processData: true
					,cache		: !debug
					,async		: false
					,type		: "GET"
					,dataType	: "text"
				});
			this.patterns[name] = oRequest.responseText;
			return new Pattern(oRequest.responseText);
		}
	},
	/*
	 * Get a View factory
	 * @name, String unify name in whole page
	 * @type, String view type, one page can contains many view with same type
	 * @setter
	 */
	view: function(type, vn, conf, setter){
		if(!this.isString(type))
			throw "I need a type to creaet/retrieve a view! - name:" + vn
					+ "\n\tconf:" + weboo.dump(conf);

		if(this.undefined(this.views[type])){	// create new type
			if(!setter)
				return null;
			this.views[type] = new Object();
			this.views[type].setter = setter;
			this.views[type].instances = new Object();
		}
		if(vn){	// if delclare a name, try to return a instance.
			var v = this.views[type].instances[vn];
			if(v) return v;
		}
		else if(null==vn)	// just declare a view
			return;
		// create a new view
		var v = new View(vn,type);
		this.views[type].setter.setup(v,(conf ? conf : {}));
		if(!v.anonymous)	// if view with a name, store it
			this.views[type].instances[v.name] = v;
		return v;
	},

	ajax: function(conf){
		conf.error = ajaxError;
		$.ajax(conf);
	},
	
	findView: function(type,name){
		var vs = this.views[type];
		if(vs)
			return vs[name];
		return null;
	},
	
	fire: function(event){
		event.stopPropagation();
		var tar = event.data;
		try{
			tar.view[tar.fn](event,tar.params);
		}catch(E){
			throw "fire : " + tar.view.fullName() + "->" + tar.fn + "() !!! " + E;
		}
	},
	
	stopEvent: function(event){
		event.stopPropagation();
	},
	
	invoke: function(obj,fn,param){
		if(obj && fn && obj[fn])
			return obj[fn](param);
		else
			return param;
	},

	/*
	 * Utilitys
	 */
	undefined: function (o){
		return (typeof o)=="undefined";
	},
	
	isBoolean: function(o){
		return "boolean"==(typeof o);
	},
	
	isNumber: function(o){
		return "number"==(typeof o);
	},
	
	isFunction: function(o){
		return o instanceof Function
	},
	
	isString: function (o){
		return "string"==(typeof o);
	},

	isObject: function (o){
		return (typeof o)=="object";
	},

	isArray: function(o){
		return o instanceof Array;
	},

	isPattern: function (o){
		return o && o._weboo_pattern;
	},

	isView: function (o){
		return o && o._weboo_view;
	},

	isjQuery: function (o){
		return o && o.jquery;
	},

	isElement: function (o){
		return o && o.nodeName && o.nodeType;
	},

	isEmpty: function(s){
		if(!s) return true;if(null==s || s.length==0)
			return true;
		return false;
	},

	isBlank: function (s){if(this.isEmpty(s))return true;
		for(i=0;i<s.length;i++)if(s.charCodeAt(i)>32)return false;
		return true;
	},

	codeIsNumber: function(code){
		return code>47 && code<58;
	},

	dump: function(obj){
		if(null==obj)
			return "NULL";
		if(this.undefined(obj))
			return "undefined";
		var re = "";
		if(this.isArray(obj)){
			if(obj.length==1){
				re = "[" + this.dump(obj[0]); + "]";
			}
			else{
				re = "[";
				for(var k in obj)
					if(this.isString(obj[k]))
						re += "\n(" + k + "): \"" + obj[k] + "\"";
					else if(this.isObject(obj[k]))
						re += "\n(" + k + "): " + this.dump(obj[k]);
					else
						re += "\n(" + k + "): " + obj[k];
				re += "\n]";
			}
		}
		else if(this.isElement(obj)){
			re = "<" + obj.nodeName + ">";
		}
		else if(this.isObject(obj)){
			var re = "{";   var first = true;
			for(var k in obj){
				if(!first) re += ",";
				if(this.isString(obj[k]))
					re += k + ": \"" + obj[k] + "\"";
				else if(this.isObject(obj[k]))
					re += k + ": " + this.dump(obj[k]);
				else
					re += k + ": " + obj[k];
				first = false;
			}
			re += "}";
		}else
			re += obj;
		return re
	},

	startsWith: function(str,sub){
		if(!str || !sub) return false;
		if(sub.length==1){
			if(str.length==0)return false;
			return str.charAt(0)==sub.charAt(0);
		}
		return str.substring(0,sub.length)==sub;
	},

	endsWith: function(str,sub){
		if(!str || !sub) return false;
		if(sub.length==1){
			if(str.length==0)return false;
			return str.charAt(str.length-1)==sub.charAt(0);
		}
		return str.substring(str.length-sub.length,str.length)==sub;
	},

	dup: function(str,n){
		var re = "";
		for(var i=0;i<n;i++)
			re+=str;
		return re;
	},

	alignr: function(s,len,chr){
		if(!s && s!=0) return null;
		chr = chr.toString();
		chr = !chr ? '0'
					: chr.length>1 ?
						chr.charAt(0)
						: chr;
		s = s.toString();
		if(s.length>=len) return s;
		return this.dup(chr,len - s.length) + s;
	},

	/*
	 * Get or set a value to a Element, the value should be a string
	 * it will set to element as text.
	 * @ele , Element
	 * @v, String | Number | Boolean
	 */
	val: function(ele,v){
		if(weboo.isjQuery(ele))
			ele = ele[0];
		if(!weboo.isElement(ele))
			throw "weboo.val() first param must by Element | jQuery!!!";
		var nn = ele.nodeName.toUpperCase();
		if(weboo.undefined(v)){	// get value from element
			if(nn=="INPUT"){
				var tt = ele.type.toUpperCase();
				return tt=="CHECKBOX" ? 
						ele.checked
						: ele.value;
			}
			else if(nn=="TEXTAREA" || nn=="SELECT")
				return ele.value;
			else
				return $.trim($(ele).text());
		}
		else{ // set value
			v = v.toString();
			if(nn=="INPUT"){
				var tt = ele.type.toUpperCase();
				if(tt=="CHECKBOX" || tt=="RADIO"){
					ele.checked = true;
				}
				else
					ele.value = v;
			}
			else if(nn=="TEXTAREA" || nn=="SELECT")
				ele.value = v;
			else
				$(ele).text(v);
		}
	},

	valobj: function(ele,attName,attValue){
		var name = (!attName ? "plugid" : attName);
		var exp = (!attValue ? "[" + name + "]" : "[" + name + "]='"+attValue+"'");
		var pp = $(ele).find(exp);
		if(pp.size()==0) return null;
		var re = new Object();
		for(var i=0;i<pp.size();i++){
			var v = weboo.val(pp.get(i));
			if(v)
				re[$(pp.get(i)).attr(name)] = v;
		}
		return re;
	}
	
}; // ~ end weboo prototype

/*
 * Add values to one object as attribute, with, name/Array<value>
 * provide keys,each,set,add,get,clear method
 */
function assembling(o){
	if(!o)
		o = new Object();
	if(!o.values)
		o.values = new Object();
	
	// add methods ...

	o.each= function(callback,k){
		if(!k){
			if(!callback) return;
			for(var key in this.values)
				callback(key,this);
		}
		else{
			var arr = this.values[k];
			if(arr)
				for(var i in arr)
					callback(arr[i],k);
		}
	};

	o.eachValue= function(callback){
		this.each(function(k,vs){
			vs.each(callback,k);
		});
	};

	o.size= function(k){
		var arr = this.values[k];
		return arr && arr.length ? arr.length : 0;
	}

	o.keys= function(){
		var re = new Array();
		this.each(function(k){
				re.push(k);
			});
		return re;
	};

	o.clear= function(k){
		if(!k)	// clear all
			this.each(function(k,me){
				me.clear(k);
			});
		else if(!this.values[k])
			return this;
		this.values[k] = null;
		return this;
	};

	o.contains= function(k){
		return !weboo.undefined(this.values[k]);
	};

	o.remove= function(k,callback){
		if(this.contains(k)){
			this.each(callback,k);
			delete this.values[k];
		}
	}

	o.get= function(k,i){
		var vs = this.values[k];
		if(!vs) return null;
		if(i || i==0)
			return vs[i];
		return vs;
	};

	o.first= function(k){
		return this.get(k,0);
	};

	o.last = function(k){
		return this.get(k,this.size()-1);
	};

	/*
	 * return a string
	 */
	o.val= function(k){
		var arr = this.values[k];
		if(!arr || arr.length==0) return null;
		if(arr.length==1)
			return arr[0];
		var re = "";
		for(var i in arr)
			re += "," + arr[i];
		return re.substring(1);
	}

	o.set = function(k,v){
		return this.clear(k).add(k,v);
	};

	o.add= function(k,v){
		if(!this.values[k]){
			if(!v && v!=0 & v!=false){
				this.values[k] = null;
				return this;
			}
			this.values[k] = new Array();
		}
		/*if(weboo.isArray(v))
			for(var i in v)
				this.values[k].push(v[i]);
		else*/
		this.values[k].push(v);
		return this;
	};
	/* return assembling object */
	return o;
};

/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 * Pattern class, cache a real HTML DOM tree, and a group of Name/Element values.
 * Client can focus on the element he/she really care about without flaring horn pestering.
 */
var Pattern = function(s){
	return this instanceof Pattern ? this.init(s) : new Pattern(s);
};

Pattern.prototype = {
	_weboo_pattern: "1.0",
	// init all document
	init: function(s){
		if(!s) throw "Can not init Pattern by null";
		// find all plug points
		var values = assembling();
		$(s).find("[plugid]").each(function(i,ele){
			var key = ele.getAttribute("plugid");
			values.set(key,null);
		});
		// save
		this.values = values;
		this.html = s;
		;
	},

	clone: function(){
		return new Pattern(this.html);
	},

	/*
	 * @gasket is String(HTML) | Element | jQuery | Pattern.
	 * @to, if gasket is a Pattern, this param is required. current pattern add self to it.
	 * return jQuery for current root element
	render2: function(gasket,to){
		if(!gasket) throw "I don't know where to append!";

		var me = this.render();
		if(weboo.isjQuery(gasket))
			gasket.append(me);
		else if(weboo.isPattern(gasket) || weboo.isView(gasket)){
			if(!to)	throw "Gasket is a pattern/view, but I don't know where!"
			gasket.add(to,this);
		}
		else
			$(gasket).append(me);
		return me;
	},*/

	/*
	 * return jQuery object by given values.
	 * Pattern values can only be: String | boolean | number | Pattern | View, another object will
	 * be take as String.
	 */
	render: function(){
		var jq = $(this.html);
		var pps = jq.find("[plugid]");
		for(var i=0;i<pps.size();i++){
			var ele = pps.get(i);
			var key = ele.getAttribute("plugid");
			var arr = this.values.get(key);
			if(arr)
				for(var z=0;z<arr.length;z++){
					var item = arr[z];
					if(weboo.isPattern(item))
						throw "PatternNested! please wrap as Pattern->View(Pattern)!";
					else if(weboo.isView(item))
						$(ele).append(item.render());
					else{
						weboo.val(ele,item);
					}
			}
		}
		return jq;
	},

	set: function(k,v){
		var vv = this.values.size(k)>0;
		if(vv){ // check ...
			if(weboo.isView(v))
				vv = false;
			else{
				// If all values in current PP is View, set value to them
				this.values.each(function(view){
					vv = vv && weboo.isView(view);
				},k);
			}
		}
		if(vv){
			this.values.each(function(view){
				view.data(v);
			},k);
		}
		else
			this.values.set(k,v);
		return this;
	},

	setObject: function(obj){if(!obj) return;
		for(var k in obj)
			this.set(k,obj[k]);
		return this;
	},
	
	add: function(k,v){
		if(this.values.contains(k))
			this.values.add(k,v);
		return this;
	},

	each: function(callback,k){
		this.values.each(function(v1,v2){
			callback(v1,v2);
		},k);
	}
};

/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 * View object will be setup by view setter, view setter is a customized JavascripObject
 * with one method "setup", this method will be invoked when create a view, one param
 * will be passed, which indicate a View Object
 *
 * View object will has those methods as default:
 *		draw(HTMLString | Element | jQuery
 *					| {to:[plugid],pattern:Pattern Object}
 *					| {to:[plugid],pattern:[pattern name],view:View Object})
 *		show(option)
 *		hide(option)
 *		
 * And view object expect view setter can set
 * attributes:
 *		patternName
 * method:
 *		data()					// return a JavaScript Object to indicate the post data
 *								// or String, if return a Object, please refer
 *								// http://docs.jquery.com/Ajax/jQuery.ajax#options -> data
 *
 * #implement latter
 * View object will provide default animation effect to show/hide view. If client wanna spcial
 * effect, it better give the setting in view setter "setup" method.
 */
var View = function(name,type){
	this.type = type;
	if(name)
		this.name = name;
	else
		this.anonymous = true;	// anonymous view without name
	this.children = assembling();
	this.values =  assembling();
};

View.prototype = {
	_weboo_view: "1.0",
	
	fullName: function(){
		return this.type + "->" + this.name;
	},
	
	pattern: function(){if(weboo.undefined(this.patternName)) throw "I nee a pattern name - " + this.type + "." + this.name;
		return weboo.pattern(this.patternName);
	},
	
	depose: function(){
		if(this.jq){
			try{
				var me = this.jq.remove();
				this.jq = null;
				return me;
			}catch(E){this.jq = null;}
		}
		return null;
	},
	
	/*
	 * @context: {type,name}=>view | will be nest as jQuery
	 */
	find: function(exp,context){
		if(!context){
			if(!this.jq) throw "Before find, you must render the view at first!";
			return this.jq.find(exp);
		}
		else if(!weboo.undefined(context.type) && !weboo.undefined(context.name)){
			var v = weboo.findView(exp.type,exp.name);
			if(v)
				return v.find(exp.target);
			return null;
		}
		return $(context).find(exp);
	},

	getDom: function(k){
		return this.find("[plugid='" + k + "']");
	},
	
	append: function(k,v){if(!weboo.isView(v)) throw "Append!!! - I am ["+this.fullName()+"]:[" + k + "] - \n " + weboo.dump(v);
		this.children.add(k,v);
		v.gasket = this;
		v.to = k;
		return this;
	},
	
	remove: function(k){
		this.children.remove(k,function(view){
				view.depose();
			});
		return this;
	},

	child: function(k,i){
		return this.children.get(k,i);
	},

	firstChild: function(k){
		return this.children.first(k);
	},
	
	lastChild: function(k){
		return this.children.last(k);
	},
	
	/* std setting */
	loadSetting: function(conf){
		this.cached = weboo.isBoolean(conf.cached) ? conf.cached : false;
		this.gasket = conf.gasket;
		this.to = conf.to;
		this.patternName = conf.patternName;
		this.speed = conf.speed ? 
							conf.speed 
							: {show:300,hide:300};
		this.post = conf.post;
		this.getter = conf.getter;
		this.combine = conf.children;
		this.events = conf.events;
		/*
		 * add all functions in conf, include
		 * beforeRender , afterRender
		 */
		 for(var func in conf)
			 if(weboo.isFunction(conf[func]))
				this[func] = conf[func];
		// this.beforeRender = conf.beforeRender;
		// this.afterRender = conf.afterRender;
	},

	init: function(){
		if(this.done) return;
		// combine self children 
		if(this.combine)
			for(var i in this.combine){
				var sub = this.combine[i];
				this.append(sub.to,$.weboo.view(sub.type,this.name + "." + sub.name,sub.conf));
			}
		// init all children
		this.children.eachValue(function(view){
			view.init();
		});
		this.done = true;
	},
	
	/*
	 * return jQuery object
	 */
	render: function(){
		// release all resources
		this.depose();
		// combine all sub views
		this.init();
		// Before render: client can override default by set this.jq.
		this.jq = weboo.invoke(this,"beforeRender");
		/* Default render */
		if(!this.jq){
			var p = this.pattern();
			// install all children to pattern
			this.children.each(function(key,chd){
				var arr = chd.get(key);
				if(arr)
					for(var i in arr)
						p.add(key,arr[i]);
			});
			// set value to pattern
			if(this.values){
				var keys = this.values.keys();
				for(var i in keys){
					var key = keys[i];
					if(key=="questions")
						var s = 99;
					p.set(key,this.getValue(key));	
				}
			}
			this.jq = p.render();
		}
		this.bindEvents();
		// AFTER RENDER: client can do anything he want.
		weboo.invoke(this,"afterRender")
		return this.jq;
	},
	
	bindEvents: function(){
		if(this.events)
			for(var i in this.events){
				var e = this.events[i];
				if(!e.context)
					this.find("[plugid='"+e.to+"']").bind(e.type,{view:this,fn:e.func},$.weboo.fire);
				else
					this.find(e.to,e.context).bind(e.type,{view:this,fn:e.func},$.weboo.fire);
			}
	},
	
	/* GETTER: customized getter */
	getValue: function(key){
		return weboo.invoke(this.getter,key,this.values.val(key));
	},

	draw: function(gasket,to){
		this.gasket = gasket;
		this.to = to;
		return this.redraw(gasket,to);
	},

	/*
	 * return jQuery as me
	 */
	redraw: function(){if(!this.gasket) throw "I don't know where to draw!";
		this.render();
		if(weboo.isView(this.gasket)){
			if(!this.to)	throw "Gasket is a View, but I don't know where!"
			this.gasket.find("[plugid='"+this.to+"']").append(this.jq);
		}
		else{
			$(this.gasket).append(this.jq);
		}
		this.show();
	},

	show: function(callback){
		if(this.jq)
			this.jq.fadeIn(this.speed.show,callback);
		else if(callback)
			callback();
		return this.jq;
	},

	hide: function(callback){
		if(this.jq)
			this.jq.fadeOut(this.speed.hide,callback);
		else if(callback)
			callback();
		return this.jq;
	},
	
	blank: function(){
		this.children.eachValue(function(chdView){
			chdView.blank();
		});
		this.values.clear();
	},

	/*
	 * Default data IO 
	 */
	data: function(d){
		if(!d){	// get data
			if(!weboo.isObject(this.post)){
				var pp = new Object();
				this.pattern().each(function(k){pp[k]=null});
				this.post = pp;
			}
			for(var k in this.post)
				this.post[k] = this.val(k);
			return this.post;
		}
		else{		// set data
			for(var k in d)
				this.values.set(k,d[k]);
		}
		return this;
	},
	
	/*
	 * @k, string indicate the plugid of current view.
	 */
	val: function(k,v){
		/* get value */
		if(weboo.undefined(v)){
			// From sub view
			if(this.children.contains(k)){
				var re = "";
				this.children.each(function(view){
					re += "," + view.data();
				},k);
				if(re.length>0)
					return re.substring(1);
				return null;
			}
			// From pattern
			if(!this.jq)
				return null;
			var o = this.jq.find("[plugid='"+k+"']");
			if(o.size()==0)
				return null;
			return weboo.val(o.get(0));
		/* set value */
		}else if(this.jq){
			weboo.val(this.jq.find("[plugid='"+k+"']"),v);
		}
	},

	getInt: function(k,def){
		var v = this.val(k);
		if(v*1 == v)
			return parseInt(v);
		return def ? def : -1;
	}
	
};
// Install weboo to jQuery
$.weboo = new Weboo();
var weboo = $.weboo;
})(jQuery);